/*
* Copyright (c) Microsoft Corporation.  All Rights Reserved.
*
* Public Mock10 API
*/
#pragma once
#include "Mock10.h"
#include "DetourMethod.h"
#include "MockFunction.h"
#include "DetourShim.h"

#pragma push_macro("Assert")
#undef Assert

#pragma push_macro("SEALED")
#undef SEALED

#if _MSC_VER >= 1400
# define SEALED sealed
#else
# define SEALED
#endif

namespace Mock10
{
    namespace Private
    {
        // The MockFunctionImpl class expects a TPrototype* as an argument to its constructor, but member functions are intrinsically already pointers.
        // So if we truly pass a TPrototype* to the MockFunctionImpl class for member functions, we'd have a pointer-to-pointer-to-member-function, which isn't what we want.
        // This class helps us treat member function pointers and regular function pointers in the same manner internally.
        template <typename T>
        struct MemberAsPointerToPointer
        {
            // returns TPrototype as TPrototype*, which results in detours treating static function pointers and member function pointers in the same manner (what we do want)
            static T* Result(T& t)
            {
                return reinterpret_cast<T*>(reinterpret_cast<void*&>(t));
            }
        };

        // Converts a function prototype to a prototype which takes a reference to the class as its first parameter
        template <typename TPrototype>
        struct MockMethodPrototype
        {
            typedef typename Private::DetourMethod<TPrototype>::Prototype DetourPrototype;
        };

        // This specialization helps compilation fail early with a valuable error message when users attempt to call Mock::Method(pszDll, pszQuery) without
        // explicitly specifying a TPrototype (Mock::Method<MyPrototype>(pszDll, pszQuery)).  Without this specialization, the error message is extremely confusing.
        template <>
        struct MockMethodPrototype<const wchar_t*>;
    }

    template <typename TPrototype, typename TDetourPrototype = typename Private::MockMethodPrototype<TPrototype>::DetourPrototype,
              typename TDetourFunctionImpl = Private::DetourFunction<TPrototype, TDetourPrototype>>
    class MockMethod SEALED : public MockFunctionImpl<TPrototype, TDetourPrototype, TDetourFunctionImpl>
    {
    template <typename TMockImpl>
    friend class Private::ExternalMockFunctionInstanceImpl;

    public:
        explicit MockMethod(TPrototype targetFunction)
        : MockFunctionImpl<TPrototype, TDetourPrototype>(Private::MemberAsPointerToPointer<TPrototype>::Result(targetFunction))
        {
        }

        MockMethod(TPrototype targetFunction, TDetourPrototype* pDetourPrototype)
        : MockFunctionImpl<TPrototype, TDetourPrototype>(Private::MemberAsPointerToPointer<TPrototype>::Result(targetFunction), pDetourPrototype)
        {
        }

        template <typename TFunctor>
        MockMethod(TPrototype targetFunction, const TFunctor& detourFunctor)
        : MockFunctionImpl<TPrototype, TDetourPrototype>(Private::MemberAsPointerToPointer<TPrototype>::Result(targetFunction), detourFunctor)
        {
        }

        MockMethod(const wchar_t* pszTargetBinary, const wchar_t* pszTargetQuery, unsigned long expectedMatchCount = 1)
        : MockFunctionImpl<TPrototype, TDetourPrototype>(pszTargetBinary, pszTargetQuery, expectedMatchCount)
        {
        }

        class Target: public TargetFunction
        {
        template <typename TPrototype, typename TDetourPrototype, typename TDetourFunctionImpl>
        friend class MockMethod;

        template <typename TPrototype>
        friend class ExternalMockMethod;

        public:
            Target(const Target& other)
            : TargetFunction(other)
            , m_offset(other.m_offset)
            , m_memberFunction(other.m_memberFunction)
            {
            #if !defined(NO_MOCK_THROW)
                HRESULT hr = GetStatus();
                if (FAILED(hr))
                {
                    Private::Throw::MockException(hr);
                }
            #endif
            }

        private:
            #define MOCK_METHOD_TARGET_NAMED_ARGS_0 typename WEX::Common::ParameterTypes<TPrototype>::ClassType& classInstance
            #define MOCK_METHOD_TARGET_NAMED_ARGS_1 typename WEX::Common::ParameterTypes<TPrototype>::ClassType& classInstance, typename WEX::Common::ParameterTypes<TPrototype>::Parameter1 p1
            #define MOCK_METHOD_TARGET_NAMED_ARGS_2 typename WEX::Common::ParameterTypes<TPrototype>::ClassType& classInstance, typename WEX::Common::ParameterTypes<TPrototype>::Parameter1 p1, typename WEX::Common::ParameterTypes<TPrototype>::Parameter2 p2
            #define MOCK_METHOD_TARGET_NAMED_ARGS_3 typename WEX::Common::ParameterTypes<TPrototype>::ClassType& classInstance, typename WEX::Common::ParameterTypes<TPrototype>::Parameter1 p1, typename WEX::Common::ParameterTypes<TPrototype>::Parameter2 p2, typename WEX::Common::ParameterTypes<TPrototype>::Parameter3 p3
            #define MOCK_METHOD_TARGET_NAMED_ARGS_4 typename WEX::Common::ParameterTypes<TPrototype>::ClassType& classInstance, typename WEX::Common::ParameterTypes<TPrototype>::Parameter1 p1, typename WEX::Common::ParameterTypes<TPrototype>::Parameter2 p2, typename WEX::Common::ParameterTypes<TPrototype>::Parameter3 p3, typename WEX::Common::ParameterTypes<TPrototype>::Parameter4 p4
            #define MOCK_METHOD_TARGET_NAMED_ARGS_5 typename WEX::Common::ParameterTypes<TPrototype>::ClassType& classInstance, typename WEX::Common::ParameterTypes<TPrototype>::Parameter1 p1, typename WEX::Common::ParameterTypes<TPrototype>::Parameter2 p2, typename WEX::Common::ParameterTypes<TPrototype>::Parameter3 p3, typename WEX::Common::ParameterTypes<TPrototype>::Parameter4 p4, typename WEX::Common::ParameterTypes<TPrototype>::Parameter5 p5
            #define MOCK_METHOD_TARGET_NAMED_ARGS_6 typename WEX::Common::ParameterTypes<TPrototype>::ClassType& classInstance, typename WEX::Common::ParameterTypes<TPrototype>::Parameter1 p1, typename WEX::Common::ParameterTypes<TPrototype>::Parameter2 p2, typename WEX::Common::ParameterTypes<TPrototype>::Parameter3 p3, typename WEX::Common::ParameterTypes<TPrototype>::Parameter4 p4, typename WEX::Common::ParameterTypes<TPrototype>::Parameter5 p5, typename WEX::Common::ParameterTypes<TPrototype>::Parameter6 p6
            #define MOCK_METHOD_TARGET_NAMED_ARGS_7 typename WEX::Common::ParameterTypes<TPrototype>::ClassType& classInstance, typename WEX::Common::ParameterTypes<TPrototype>::Parameter1 p1, typename WEX::Common::ParameterTypes<TPrototype>::Parameter2 p2, typename WEX::Common::ParameterTypes<TPrototype>::Parameter3 p3, typename WEX::Common::ParameterTypes<TPrototype>::Parameter4 p4, typename WEX::Common::ParameterTypes<TPrototype>::Parameter5 p5, typename WEX::Common::ParameterTypes<TPrototype>::Parameter6 p6, typename WEX::Common::ParameterTypes<TPrototype>::Parameter7 p7
            #define MOCK_METHOD_TARGET_NAMED_ARGS_8 typename WEX::Common::ParameterTypes<TPrototype>::ClassType& classInstance, typename WEX::Common::ParameterTypes<TPrototype>::Parameter1 p1, typename WEX::Common::ParameterTypes<TPrototype>::Parameter2 p2, typename WEX::Common::ParameterTypes<TPrototype>::Parameter3 p3, typename WEX::Common::ParameterTypes<TPrototype>::Parameter4 p4, typename WEX::Common::ParameterTypes<TPrototype>::Parameter5 p5, typename WEX::Common::ParameterTypes<TPrototype>::Parameter6 p6, typename WEX::Common::ParameterTypes<TPrototype>::Parameter7 p7, typename WEX::Common::ParameterTypes<TPrototype>::Parameter8 p8
            #define MOCK_METHOD_TARGET_NAMED_ARGS_9 typename WEX::Common::ParameterTypes<TPrototype>::ClassType& classInstance, typename WEX::Common::ParameterTypes<TPrototype>::Parameter1 p1, typename WEX::Common::ParameterTypes<TPrototype>::Parameter2 p2, typename WEX::Common::ParameterTypes<TPrototype>::Parameter3 p3, typename WEX::Common::ParameterTypes<TPrototype>::Parameter4 p4, typename WEX::Common::ParameterTypes<TPrototype>::Parameter5 p5, typename WEX::Common::ParameterTypes<TPrototype>::Parameter6 p6, typename WEX::Common::ParameterTypes<TPrototype>::Parameter7 p7, typename WEX::Common::ParameterTypes<TPrototype>::Parameter8 p8, typename WEX::Common::ParameterTypes<TPrototype>::Parameter9 p9
            #define MOCK_METHOD_TARGET_NAMED_ARGS_10 typename WEX::Common::ParameterTypes<TPrototype>::ClassType& classInstance, typename WEX::Common::ParameterTypes<TPrototype>::Parameter1 p1, typename WEX::Common::ParameterTypes<TPrototype>::Parameter2 p2, typename WEX::Common::ParameterTypes<TPrototype>::Parameter3 p3, typename WEX::Common::ParameterTypes<TPrototype>::Parameter4 p4, typename WEX::Common::ParameterTypes<TPrototype>::Parameter5 p5, typename WEX::Common::ParameterTypes<TPrototype>::Parameter6 p6, typename WEX::Common::ParameterTypes<TPrototype>::Parameter7 p7, typename WEX::Common::ParameterTypes<TPrototype>::Parameter8 p8, typename WEX::Common::ParameterTypes<TPrototype>::Parameter9 p9, typename WEX::Common::ParameterTypes<TPrototype>::Parameter10 p10
            #define MOCK_METHOD_TARGET_NAMED_ARGS_11 typename WEX::Common::ParameterTypes<TPrototype>::ClassType& classInstance, typename WEX::Common::ParameterTypes<TPrototype>::Parameter1 p1, typename WEX::Common::ParameterTypes<TPrototype>::Parameter2 p2, typename WEX::Common::ParameterTypes<TPrototype>::Parameter3 p3, typename WEX::Common::ParameterTypes<TPrototype>::Parameter4 p4, typename WEX::Common::ParameterTypes<TPrototype>::Parameter5 p5, typename WEX::Common::ParameterTypes<TPrototype>::Parameter6 p6, typename WEX::Common::ParameterTypes<TPrototype>::Parameter7 p7, typename WEX::Common::ParameterTypes<TPrototype>::Parameter8 p8, typename WEX::Common::ParameterTypes<TPrototype>::Parameter9 p9, typename WEX::Common::ParameterTypes<TPrototype>::Parameter10 p10, typename WEX::Common::ParameterTypes<TPrototype>::Parameter11 p11
            #define MOCK_METHOD_TARGET_NAMED_ARGS_12 typename WEX::Common::ParameterTypes<TPrototype>::ClassType& classInstance, typename WEX::Common::ParameterTypes<TPrototype>::Parameter1 p1, typename WEX::Common::ParameterTypes<TPrototype>::Parameter2 p2, typename WEX::Common::ParameterTypes<TPrototype>::Parameter3 p3, typename WEX::Common::ParameterTypes<TPrototype>::Parameter4 p4, typename WEX::Common::ParameterTypes<TPrototype>::Parameter5 p5, typename WEX::Common::ParameterTypes<TPrototype>::Parameter6 p6, typename WEX::Common::ParameterTypes<TPrototype>::Parameter7 p7, typename WEX::Common::ParameterTypes<TPrototype>::Parameter8 p8, typename WEX::Common::ParameterTypes<TPrototype>::Parameter9 p9, typename WEX::Common::ParameterTypes<TPrototype>::Parameter10 p10, typename WEX::Common::ParameterTypes<TPrototype>::Parameter11 p11, typename WEX::Common::ParameterTypes<TPrototype>::Parameter12 p12
            #define MOCK_METHOD_TARGET_NAMED_ARGS_13 typename WEX::Common::ParameterTypes<TPrototype>::ClassType& classInstance, typename WEX::Common::ParameterTypes<TPrototype>::Parameter1 p1, typename WEX::Common::ParameterTypes<TPrototype>::Parameter2 p2, typename WEX::Common::ParameterTypes<TPrototype>::Parameter3 p3, typename WEX::Common::ParameterTypes<TPrototype>::Parameter4 p4, typename WEX::Common::ParameterTypes<TPrototype>::Parameter5 p5, typename WEX::Common::ParameterTypes<TPrototype>::Parameter6 p6, typename WEX::Common::ParameterTypes<TPrototype>::Parameter7 p7, typename WEX::Common::ParameterTypes<TPrototype>::Parameter8 p8, typename WEX::Common::ParameterTypes<TPrototype>::Parameter9 p9, typename WEX::Common::ParameterTypes<TPrototype>::Parameter10 p10, typename WEX::Common::ParameterTypes<TPrototype>::Parameter11 p11, typename WEX::Common::ParameterTypes<TPrototype>::Parameter12 p12, typename WEX::Common::ParameterTypes<TPrototype>::Parameter13 p13
            #define MOCK_METHOD_TARGET_NAMED_ARGS_14 typename WEX::Common::ParameterTypes<TPrototype>::ClassType& classInstance, typename WEX::Common::ParameterTypes<TPrototype>::Parameter1 p1, typename WEX::Common::ParameterTypes<TPrototype>::Parameter2 p2, typename WEX::Common::ParameterTypes<TPrototype>::Parameter3 p3, typename WEX::Common::ParameterTypes<TPrototype>::Parameter4 p4, typename WEX::Common::ParameterTypes<TPrototype>::Parameter5 p5, typename WEX::Common::ParameterTypes<TPrototype>::Parameter6 p6, typename WEX::Common::ParameterTypes<TPrototype>::Parameter7 p7, typename WEX::Common::ParameterTypes<TPrototype>::Parameter8 p8, typename WEX::Common::ParameterTypes<TPrototype>::Parameter9 p9, typename WEX::Common::ParameterTypes<TPrototype>::Parameter10 p10, typename WEX::Common::ParameterTypes<TPrototype>::Parameter11 p11, typename WEX::Common::ParameterTypes<TPrototype>::Parameter12 p12, typename WEX::Common::ParameterTypes<TPrototype>::Parameter13 p13, typename WEX::Common::ParameterTypes<TPrototype>::Parameter14 p14
            #define MOCK_METHOD_TARGET_NAMED_ARGS_15 typename WEX::Common::ParameterTypes<TPrototype>::ClassType& classInstance, typename WEX::Common::ParameterTypes<TPrototype>::Parameter1 p1, typename WEX::Common::ParameterTypes<TPrototype>::Parameter2 p2, typename WEX::Common::ParameterTypes<TPrototype>::Parameter3 p3, typename WEX::Common::ParameterTypes<TPrototype>::Parameter4 p4, typename WEX::Common::ParameterTypes<TPrototype>::Parameter5 p5, typename WEX::Common::ParameterTypes<TPrototype>::Parameter6 p6, typename WEX::Common::ParameterTypes<TPrototype>::Parameter7 p7, typename WEX::Common::ParameterTypes<TPrototype>::Parameter8 p8, typename WEX::Common::ParameterTypes<TPrototype>::Parameter9 p9, typename WEX::Common::ParameterTypes<TPrototype>::Parameter10 p10, typename WEX::Common::ParameterTypes<TPrototype>::Parameter11 p11, typename WEX::Common::ParameterTypes<TPrototype>::Parameter12 p12, typename WEX::Common::ParameterTypes<TPrototype>::Parameter13 p13, typename WEX::Common::ParameterTypes<TPrototype>::Parameter14 p14, typename WEX::Common::ParameterTypes<TPrototype>::Parameter15 p15
            #define MOCK_METHOD_TARGET_NAMED_ARGS_16 typename WEX::Common::ParameterTypes<TPrototype>::ClassType& classInstance, typename WEX::Common::ParameterTypes<TPrototype>::Parameter1 p1, typename WEX::Common::ParameterTypes<TPrototype>::Parameter2 p2, typename WEX::Common::ParameterTypes<TPrototype>::Parameter3 p3, typename WEX::Common::ParameterTypes<TPrototype>::Parameter4 p4, typename WEX::Common::ParameterTypes<TPrototype>::Parameter5 p5, typename WEX::Common::ParameterTypes<TPrototype>::Parameter6 p6, typename WEX::Common::ParameterTypes<TPrototype>::Parameter7 p7, typename WEX::Common::ParameterTypes<TPrototype>::Parameter8 p8, typename WEX::Common::ParameterTypes<TPrototype>::Parameter9 p9, typename WEX::Common::ParameterTypes<TPrototype>::Parameter10 p10, typename WEX::Common::ParameterTypes<TPrototype>::Parameter11 p11, typename WEX::Common::ParameterTypes<TPrototype>::Parameter12 p12, typename WEX::Common::ParameterTypes<TPrototype>::Parameter13 p13, typename WEX::Common::ParameterTypes<TPrototype>::Parameter14 p14, typename WEX::Common::ParameterTypes<TPrototype>::Parameter15 p15, typename WEX::Common::ParameterTypes<TPrototype>::Parameter16 p16
            #define MOCK_METHOD_TARGET_NAMED_ARGS_17 typename WEX::Common::ParameterTypes<TPrototype>::ClassType& classInstance, typename WEX::Common::ParameterTypes<TPrototype>::Parameter1 p1, typename WEX::Common::ParameterTypes<TPrototype>::Parameter2 p2, typename WEX::Common::ParameterTypes<TPrototype>::Parameter3 p3, typename WEX::Common::ParameterTypes<TPrototype>::Parameter4 p4, typename WEX::Common::ParameterTypes<TPrototype>::Parameter5 p5, typename WEX::Common::ParameterTypes<TPrototype>::Parameter6 p6, typename WEX::Common::ParameterTypes<TPrototype>::Parameter7 p7, typename WEX::Common::ParameterTypes<TPrototype>::Parameter8 p8, typename WEX::Common::ParameterTypes<TPrototype>::Parameter9 p9, typename WEX::Common::ParameterTypes<TPrototype>::Parameter10 p10, typename WEX::Common::ParameterTypes<TPrototype>::Parameter11 p11, typename WEX::Common::ParameterTypes<TPrototype>::Parameter12 p12, typename WEX::Common::ParameterTypes<TPrototype>::Parameter13 p13, typename WEX::Common::ParameterTypes<TPrototype>::Parameter14 p14, typename WEX::Common::ParameterTypes<TPrototype>::Parameter15 p15, typename WEX::Common::ParameterTypes<TPrototype>::Parameter16 p16, typename WEX::Common::ParameterTypes<TPrototype>::Parameter17 p17
            #define MOCK_METHOD_TARGET_NAMED_ARGS_18 typename WEX::Common::ParameterTypes<TPrototype>::ClassType& classInstance, typename WEX::Common::ParameterTypes<TPrototype>::Parameter1 p1, typename WEX::Common::ParameterTypes<TPrototype>::Parameter2 p2, typename WEX::Common::ParameterTypes<TPrototype>::Parameter3 p3, typename WEX::Common::ParameterTypes<TPrototype>::Parameter4 p4, typename WEX::Common::ParameterTypes<TPrototype>::Parameter5 p5, typename WEX::Common::ParameterTypes<TPrototype>::Parameter6 p6, typename WEX::Common::ParameterTypes<TPrototype>::Parameter7 p7, typename WEX::Common::ParameterTypes<TPrototype>::Parameter8 p8, typename WEX::Common::ParameterTypes<TPrototype>::Parameter9 p9, typename WEX::Common::ParameterTypes<TPrototype>::Parameter10 p10, typename WEX::Common::ParameterTypes<TPrototype>::Parameter11 p11, typename WEX::Common::ParameterTypes<TPrototype>::Parameter12 p12, typename WEX::Common::ParameterTypes<TPrototype>::Parameter13 p13, typename WEX::Common::ParameterTypes<TPrototype>::Parameter14 p14, typename WEX::Common::ParameterTypes<TPrototype>::Parameter15 p15, typename WEX::Common::ParameterTypes<TPrototype>::Parameter16 p16, typename WEX::Common::ParameterTypes<TPrototype>::Parameter17 p17, typename WEX::Common::ParameterTypes<TPrototype>::Parameter18 p18
            #define MOCK_METHOD_TARGET_NAMED_ARGS_19 typename WEX::Common::ParameterTypes<TPrototype>::ClassType& classInstance, typename WEX::Common::ParameterTypes<TPrototype>::Parameter1 p1, typename WEX::Common::ParameterTypes<TPrototype>::Parameter2 p2, typename WEX::Common::ParameterTypes<TPrototype>::Parameter3 p3, typename WEX::Common::ParameterTypes<TPrototype>::Parameter4 p4, typename WEX::Common::ParameterTypes<TPrototype>::Parameter5 p5, typename WEX::Common::ParameterTypes<TPrototype>::Parameter6 p6, typename WEX::Common::ParameterTypes<TPrototype>::Parameter7 p7, typename WEX::Common::ParameterTypes<TPrototype>::Parameter8 p8, typename WEX::Common::ParameterTypes<TPrototype>::Parameter9 p9, typename WEX::Common::ParameterTypes<TPrototype>::Parameter10 p10, typename WEX::Common::ParameterTypes<TPrototype>::Parameter11 p11, typename WEX::Common::ParameterTypes<TPrototype>::Parameter12 p12, typename WEX::Common::ParameterTypes<TPrototype>::Parameter13 p13, typename WEX::Common::ParameterTypes<TPrototype>::Parameter14 p14, typename WEX::Common::ParameterTypes<TPrototype>::Parameter15 p15, typename WEX::Common::ParameterTypes<TPrototype>::Parameter16 p16, typename WEX::Common::ParameterTypes<TPrototype>::Parameter17 p17, typename WEX::Common::ParameterTypes<TPrototype>::Parameter18 p18, typename WEX::Common::ParameterTypes<TPrototype>::Parameter19 p19
            #define MOCK_METHOD_TARGET_NAMED_ARGS_20 typename WEX::Common::ParameterTypes<TPrototype>::ClassType& classInstance, typename WEX::Common::ParameterTypes<TPrototype>::Parameter1 p1, typename WEX::Common::ParameterTypes<TPrototype>::Parameter2 p2, typename WEX::Common::ParameterTypes<TPrototype>::Parameter3 p3, typename WEX::Common::ParameterTypes<TPrototype>::Parameter4 p4, typename WEX::Common::ParameterTypes<TPrototype>::Parameter5 p5, typename WEX::Common::ParameterTypes<TPrototype>::Parameter6 p6, typename WEX::Common::ParameterTypes<TPrototype>::Parameter7 p7, typename WEX::Common::ParameterTypes<TPrototype>::Parameter8 p8, typename WEX::Common::ParameterTypes<TPrototype>::Parameter9 p9, typename WEX::Common::ParameterTypes<TPrototype>::Parameter10 p10, typename WEX::Common::ParameterTypes<TPrototype>::Parameter11 p11, typename WEX::Common::ParameterTypes<TPrototype>::Parameter12 p12, typename WEX::Common::ParameterTypes<TPrototype>::Parameter13 p13, typename WEX::Common::ParameterTypes<TPrototype>::Parameter14 p14, typename WEX::Common::ParameterTypes<TPrototype>::Parameter15 p15, typename WEX::Common::ParameterTypes<TPrototype>::Parameter16 p16, typename WEX::Common::ParameterTypes<TPrototype>::Parameter17 p17, typename WEX::Common::ParameterTypes<TPrototype>::Parameter18 p18, typename WEX::Common::ParameterTypes<TPrototype>::Parameter19 p19, typename WEX::Common::ParameterTypes<TPrototype>::Parameter20 p20

            // Contains the layout of an actual __thiscall member function pointer: a pointer followed by a size_t for the 'this' adjustment
            // We always fill the adjustment to 0, however, because we need to perform the adjustment ourselves; otherwise sometimes
            // the compiler chooses not to perform the adjustment
            #pragma warning(push)
            #pragma warning(disable:4201) // Use of anonymous struct within a union
            union MemberFunction
            {
                TPrototype m_pFunction;
                struct
                {
                    void* m_p;
                    size_t m_dontUseOffset;
                };
            };
            #pragma warning(pop)

            template <typename TPrototype>
            struct Dispatcher;

        #define MOCK_METHOD_TARGET_IMPL(PARAMCOUNT, IS_CONST)                                                                                                                                                      \
        public:                                                                                                                                                                                                    \
            typename WEX::Common::ParameterTypes<TPrototype>::ReturnType operator()(BUILD_VARIABLE_NAME(MEMBER_PARAMETER_TYPES_CONST_, IS_CONST) BUILD_VARIABLE_NAME(MOCK_METHOD_TARGET_NAMED_ARGS_, PARAMCOUNT))  \
            {                                                                                                                                                                                                      \
                WEX_ASSERT(m_memberFunction.m_pFunction, L"");                                                                                                                                                     \
                return Dispatcher<TPrototype>::Invoke(classInstance, *this BUILD_VARIABLE_NAME(DETOUR_METHOD_ARGS_, PARAMCOUNT));                                                                                  \
            }                                                                                                                                                                                                      \
        private:                                                                                                                                                                                                   \
            BUILD_VARIABLE_NAME(MEMBER_PARAMETER_TYPES_TEMPLATE_, PARAMCOUNT)                                                                                                                                      \
            struct Dispatcher<TR (__thiscall TC::*)(BUILD_VARIABLE_NAME(PARAMETER_TYPES_ARGS_, PARAMCOUNT)) BUILD_VARIABLE_NAME(MEMBER_PARAMETER_TYPES_CONST_, IS_CONST)>                                          \
            {                                                                                                                                                                                                      \
                typedef typename WEX::Common::ParameterTypes<TPrototype> TParameterTypes;                                                                                                                          \
                static TR Invoke(BUILD_VARIABLE_NAME(MEMBER_PARAMETER_TYPES_CONST_, IS_CONST) TC& classInstance, Target& target BUILD_VARIABLE_NAME(DETOUR_FILTER_NAMED_ARGS_, PARAMCOUNT))                        \
                {                                                                                                                                                                                                  \
                    typedef typename WEX::Common::ParameterTypes<TPrototype>::ClassType ClassInstance;                                                                                                             \
                                                                                                                                                                                                                   \
                    /* Manually adjust the this pointer so we don't rely on code gen to do so */                                                                                                                   \
                    ClassInstance* pClassInstance = reinterpret_cast<ClassInstance*>(reinterpret_cast<unsigned __int64>(&classInstance) + target.m_offset);                                                        \
                    return (pClassInstance->*(target.m_memberFunction.m_pFunction))(BUILD_VARIABLE_NAME(PARAMETER_ARGS_, PARAMCOUNT));                                                                             \
                }                                                                                                                                                                                                  \
            };

        #define MOCK_METHOD_TARGET(PARAMCOUNT)        \
            MOCK_METHOD_TARGET_IMPL(PARAMCOUNT, true) \
            MOCK_METHOD_TARGET_IMPL(PARAMCOUNT, false)

        MOCK_METHOD_TARGET(0)
        MOCK_METHOD_TARGET(1)
        MOCK_METHOD_TARGET(2)
        MOCK_METHOD_TARGET(3)
        MOCK_METHOD_TARGET(4)
        MOCK_METHOD_TARGET(5)
        MOCK_METHOD_TARGET(6)
        MOCK_METHOD_TARGET(7)
        MOCK_METHOD_TARGET(8)
        MOCK_METHOD_TARGET(9)
        MOCK_METHOD_TARGET(10)
        MOCK_METHOD_TARGET(11)
        MOCK_METHOD_TARGET(12)
        MOCK_METHOD_TARGET(13)
        MOCK_METHOD_TARGET(14)
        MOCK_METHOD_TARGET(15)
        MOCK_METHOD_TARGET(16)
        MOCK_METHOD_TARGET(17)
        MOCK_METHOD_TARGET(18)
        MOCK_METHOD_TARGET(19)
        MOCK_METHOD_TARGET(20)

        #if defined(_M_IX86)
        #pragma warning(push)
        #pragma warning(disable:4127) // Conditional expression is constant

        #define MOCK_METHOD_STDCALL_TARGET_IMPL(PARAMCOUNT, IS_CONST)                                                                                                                       \
        private:                                                                                                                                                                            \
            BUILD_VARIABLE_NAME(MEMBER_PARAMETER_TYPES_TEMPLATE_, PARAMCOUNT)                                                                                                               \
            struct Dispatcher<TR (__stdcall TC::*)(BUILD_VARIABLE_NAME(PARAMETER_TYPES_ARGS_, PARAMCOUNT)) BUILD_VARIABLE_NAME(MEMBER_PARAMETER_TYPES_CONST_, IS_CONST)>                    \
            {                                                                                                                                                                               \
                typedef typename WEX::Common::ParameterTypes<TPrototype> TParameterTypes;                                                                                                   \
                static TR Invoke(BUILD_VARIABLE_NAME(MEMBER_PARAMETER_TYPES_CONST_, IS_CONST) TC& classInstance, Target& target BUILD_VARIABLE_NAME(DETOUR_FILTER_NAMED_ARGS_, PARAMCOUNT)) \
                {                                                                                                                                                                           \
                    void* pClassInstance = reinterpret_cast<void*>(reinterpret_cast<unsigned __int64>(&classInstance) + target.m_offset);                                                   \
                                                                                                                                                                                            \
                    /* The return value is too large to fit into eax, so we have to call the function with */                                                                               \
                    /* space available on the stack for the return value */                                                                                                                 \
                    if (sizeof(typename WEX::Common::ParameterTypes<TPrototype>::ReturnType) > sizeof(void*))                                                                               \
                    {                                                                                                                                                                       \
                        typedef void (__stdcall *TargetPrototype)(void* classPointer, void* pResult BUILD_VARIABLE_NAME(DETOUR_METHOD_NAMED_ARGS_, PARAMCOUNT));                            \
                                                                                                                                                                                            \
                        TargetPrototype* pTarget = reinterpret_cast<TargetPrototype*>(&target.m_memberFunction.m_p);                                                                        \
                        unsigned char result[sizeof(typename WEX::Common::ParameterTypes<TPrototype>::ReturnType)];                                                                         \
                        (*pTarget)(pClassInstance, result BUILD_VARIABLE_NAME(DETOUR_METHOD_ARGS_, PARAMCOUNT));                                                                            \
                                                                                                                                                                                            \
                        return *reinterpret_cast<typename WEX::Common::ParameterTypes<TPrototype>::ReturnType*>(result);                                                                    \
                    }                                                                                                                                                                       \
                                                                                                                                                                                            \
                    /* The return value is small enough to fit in eax, so we call the function as if it takes the class instance as its first parameter */                                  \
                    typedef typename WEX::Common::ParameterTypes<TPrototype>::ReturnType (__stdcall *TargetPrototype)(void* BUILD_VARIABLE_NAME(DETOUR_METHOD_NAMED_ARGS_, PARAMCOUNT));    \
                    TargetPrototype* pTarget = reinterpret_cast<TargetPrototype*>(&target.m_memberFunction.m_p);                                                                            \
                    return (*pTarget)(pClassInstance BUILD_VARIABLE_NAME(DETOUR_METHOD_ARGS_, PARAMCOUNT));                                                                                 \
                }                                                                                                                                                                           \
            };

        #define MOCK_METHOD_STDCALL_TARGET(PARAMCOUNT)        \
            MOCK_METHOD_STDCALL_TARGET_IMPL(PARAMCOUNT, true) \
            MOCK_METHOD_STDCALL_TARGET_IMPL(PARAMCOUNT, false)

        MOCK_METHOD_STDCALL_TARGET(0)
        MOCK_METHOD_STDCALL_TARGET(1)
        MOCK_METHOD_STDCALL_TARGET(2)
        MOCK_METHOD_STDCALL_TARGET(3)
        MOCK_METHOD_STDCALL_TARGET(4)
        MOCK_METHOD_STDCALL_TARGET(5)
        MOCK_METHOD_STDCALL_TARGET(6)
        MOCK_METHOD_STDCALL_TARGET(7)
        MOCK_METHOD_STDCALL_TARGET(8)
        MOCK_METHOD_STDCALL_TARGET(9)
        MOCK_METHOD_STDCALL_TARGET(10)
        MOCK_METHOD_STDCALL_TARGET(11)
        MOCK_METHOD_STDCALL_TARGET(12)
        MOCK_METHOD_STDCALL_TARGET(13)
        MOCK_METHOD_STDCALL_TARGET(14)
        MOCK_METHOD_STDCALL_TARGET(15)
        MOCK_METHOD_STDCALL_TARGET(16)
        MOCK_METHOD_STDCALL_TARGET(17)
        MOCK_METHOD_STDCALL_TARGET(18)
        MOCK_METHOD_STDCALL_TARGET(19)
        MOCK_METHOD_STDCALL_TARGET(20)

        #pragma warning(pop)
        #endif

        private:
            Target& operator=(const Target&); // Not implemented

            Target(HRESULT status)
            : TargetFunction(status)
            , m_offset(0)
            {
            #if !defined(NO_MOCK_THROW)
                HRESULT hr = GetStatus();
                if (FAILED(hr))
                {
                    Private::Throw::MockException(hr);
                }
            #endif

                m_memberFunction.m_p = NULL;
                m_memberFunction.m_dontUseOffset = 0;
            }

            Target(void* pMemberFunction, long thisAdjust)
            : TargetFunction(pMemberFunction)
            , m_offset(thisAdjust)
            {
            #if !defined(NO_MOCK_THROW)
                HRESULT hr = GetStatus();
                if (FAILED(hr))
                {
                    Private::Throw::MockException(hr);
                }
            #endif

                m_memberFunction.m_p = pMemberFunction;
                m_memberFunction.m_dontUseOffset = 0; // Always set this padding to zero, because we manually perform the adjustment ourselves
            }

            MemberFunction m_memberFunction;
            long m_offset;
        };

        Target GetTarget(unsigned long targetIndex = 0) const
        {
            if (FAILED(m_status.GetErrorCode()))
            {
                return Target(m_status.GetErrorCode());
            }

            if (m_detours.GetCount() <= targetIndex)
            {
                return Target(HRESULT_FROM_WIN32(ERROR_INVALID_INDEX));
            }

            Private::DetourFunctionBase& detour = m_detours[targetIndex]->GetDetour();
            return Target(detour.GetTarget(), -detour.GetThisAdjust());
        }

    private:
        MockMethod(const Private::TargetFunctionInfoArray& targetFunctions, TDetourPrototype* pDetourFunction)
        : MockFunctionImpl(targetFunctions, pDetourFunction)
        {
        }
    };
} /* namespace Mock10 */

#pragma pop_macro("SEALED")
#pragma pop_macro("Assert")